using System;
using System.Drawing;
using System.Data;
using System.Data.Odbc;
using System.Collections;

namespace StaticUtility
{
	/// <summary>
	/// Static utilities.
	/// </summary>
	public class Util
	{
		public static readonly double FourThirdsPi = 4.0*Math.PI/3.0;
		public static readonly double TwoThirdsPi = 2.0*Math.PI/3.0;
		public static readonly double HalfPi = Math.PI/2.0;
		public static readonly double ThirdPi = Math.PI/3.0;
		public static readonly double QuarterPi = Math.PI/4.0;
		public static readonly double TwoPi = Math.PI*2.0;
		public static readonly double Sqrt2 = Math.Sqrt(2.0);
		public Util(){}
		/// <summary>
		/// The color of a standard normally distributed 'z' value.
		/// </summary>
		/// <param name="z"></param>
		/// <returns></returns>
		public static Color BlueGreenGrayYellowRed(float z)
		{
			z/=0.7071f;
			if(z>1.0f)
			{
				//	arc between red and yellow.
				z = (z-1.0f)*0.59f;	//	max at 1.5708 (pi/2)
				if(z>1.5708f)
					z=1.5708f;
					
				//	Weight red with sin(z) and yellow with cos(z)
				double s = Math.Sin((double)z);
				double c = Math.Cos((double)z);

				return Color.FromArgb( (int)(Math.Min(255.0,(c+s)*255.0)), (int)(c*255.0), 0 );
			}
			else if(z>=0.0f)
			{
				int gray = (int)((1.0-z)*127.50);
				//	linear transition between yellow and gray.
				return Color.FromArgb( gray+(int)(z*125.0f), gray+(int)(z*125.0f), gray );
			}
			else if(z>-1.0f)
			{
				z=-z;
				int gray = (int)((1.0-z)*127.50);
				//	linear transition between green and gray.
				return Color.FromArgb( gray, gray+(int)(z*125.0f), gray );
			}
			else //	 z < -1.0f
			{
				//	arc between blue and green.
				z = -(z+1.0f)*0.59f;	//	max at 1.5708 (pi/2)
				if(z>1.5708f)
					z=1.5708f;
					
				//	Weight blue with sin(z) and green with cos(z)
				double s = Math.Sin((double)z);
				double c = Math.Cos((double)z);

				return Color.FromArgb( 0, (int)(c*255.0), (int)(s*255.0) );
			}
		}
		/// <summary>
		/// Outputs a string that represents the unsigned integer "num" with "padding" digits (usually led by zeros).
		/// </summary>
		/// <param name="num"></param>
		/// <param name="padding"></param>
		/// <returns></returns>
		public static string PaddedIntegerString(uint num, uint padding)
		{
			if(num >= (uint)Math.Pow(10.0,(double)padding+1.0))
				return Convert.ToString(num);
			
			string output = "";
			
			for(uint i=0; i<padding; i++)
			{
				output = Convert.ToString(num%10) + output;
				num = num/10u;
			}
			return output;
		}
		/// <summary>
		/// For a given filename with the path, this removes all the information regarding the path (if the path info exists).
		/// </summary>
		/// <param name="filepath"></param>
		/// <returns></returns>
		public static string RemovePathInfo(string filepath)
		{
			int ct = filepath.Length-2;
			while(ct>=0 && filepath[ct]!='/' && filepath[ct]!='\\')
				ct--;
			return filepath.Substring(ct+1);
		}
		/// <summary>
		/// This configures the attributes of col to be compatible with the sqlTypeString.
		/// Unsupported SQL types:
		///		DECIMAL, DEC, NUMERIC, FIXED, DATE, TIME, TIMESTAMP, YEAR, SET
		/// </summary>
		/// <param name="col">The data column.</param>
		/// <param name="sqlTypeString">The string containing the SQL type definition.</param>
		public static void SetDataColumnType(DataColumn col, string sqlTypeString)
		{
			string colTypeString = sqlTypeString.ToUpper();
			if( colTypeString.IndexOf("DATETIME")>=0 )
			{
				col.DataType = typeof(DateTime);
			}
			else if( colTypeString.IndexOf("BIT")>=0 || colTypeString.IndexOf("BOOL")>=0 )	//	implicitly includes BOOLEAN
			{
				col.DataType = typeof(bool);
			}
			else if( colTypeString.IndexOf("BIGINT")>=0 )
			{
				if( colTypeString.IndexOf("UNSIGNED")>=0 )
					col.DataType = typeof(ulong);
				else
					col.DataType = typeof(long);
			}
			else if( colTypeString.IndexOf("TINYINT")>=0 )
			{
				col.DataType=typeof(byte);
			}
			else if(colTypeString.IndexOf("SMALLINT")>=0 )
			{
				if( colTypeString.IndexOf("UNSIGNED")>=0 )
					col.DataType = typeof(ushort);
				else
					col.DataType = typeof(short);
			}
			else if( colTypeString.IndexOf("INT")>=0)		//	implicitly includes MEDIUMINT (24-bit), and INTEGER
			{
				if( colTypeString.IndexOf("UNSIGNED")>=0 )
					col.DataType = typeof(uint);
				else
					col.DataType = typeof(int);
			}
			else if( colTypeString.IndexOf("ENUM")>=0 )
			{
				col.DataType = typeof(int);
			}
			else if( colTypeString.IndexOf("FLOAT")>=0 )
			{
				col.DataType = typeof(float);
			}
			else if( colTypeString.IndexOf("REAL")>=0 || colTypeString.IndexOf("DOUBLE")>=0 )
			{
				col.DataType = typeof(float);
			}
			else if( colTypeString.IndexOf("CHAR")>=0 || colTypeString.IndexOf("TEXT")>=0)	//	implicitly includes VARCHAR, TINYTEXT, MEDIUMTEXT, LONGTEXT 
			{
				col.DataType = typeof(string);
			}
			else if( colTypeString.IndexOf("BLOB")>=0 )	//	implicitly includes TINYBLOB, MEDIUMBLOB, LONGBLOB
			{
				col.DataType = typeof(byte[]);
			}
			col.AllowDBNull = colTypeString.IndexOf("NOT NULL")<0;
		}
		/// <summary>
		/// Returns a string of n comma separated question marks.
		/// </summary>
		/// <param name="n">The number of question marks.</param>
		/// <returns></returns>
		public static string CommaSepQuestionMarks(int n)
		{
			if(n<=0)
				return "";
			else
			{
				char[] output = new char[2*n-1];
				output[0] = '?';
				int i,ii;
				for(i=1; i<n; i++)
				{
					ii = 2*i-1;
					output[ii] = ',';
					output[ii+1] = '?';
				}
				return new string(output);
			}
		}
		/// <summary>
		/// Creates a clone fo the OdbcParameter.
		/// </summary>
		/// <param name="par">OdbcParameter  The clonee.</param>
		/// <returns>odbcParameter  The cloned.</returns>
		public static OdbcParameter OdbcParameterCopy(OdbcParameter par)
		{
			OdbcParameter output = new OdbcParameter(par.ParameterName, par.OdbcType, par.Size, par.SourceColumn);
			output.IsNullable = par.IsNullable;
			output.Direction = par.Direction;
			output.SourceVersion = par.SourceVersion;
			output.Precision = par.Precision;
			return output;
		}
		/// <summary>
		/// Converts a matrix to a string.
		/// </summary>
		/// <param name="mat"></param>
		/// <returns></returns>
		public static string MatrixToString(double[,] mat)
		{
			string output = "";
			int rows = mat.GetLength(0);
			int cols = mat.GetLength(1);	
			for(int i=0; i<rows; i++)
			{
				for(int j=0; j<cols; j++)
					output = output + "\t" + Convert.ToString(mat[i,j]);
				if(i<rows-1)
					output = output + "\n";
			}
			return output;
		}
		/// <summary>
		/// Computes the angle of the segment a-->b.
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns>A value in the range [0, 2pi]</returns>
		public static double Angle(PointF a, PointF b)
		{
			double output =  Math.Atan2( (double)(b.Y-a.Y), (double)(b.X-a.X) );
			if(output<0.0)
				output+=Util.TwoPi;
			return output;
		}
		/// <summary>
		/// angle1 - angle2, in radians
		/// </summary>
		/// <param name="angle1"></param>
		/// <param name="angle2"></param>
		/// <returns></returns>
		public static double AngleDistance(double angle1, double angle2)
		{
			double output = angle1-angle2;
			if(output > Math.PI)
				output -= TwoPi;
			else if(output < -Math.PI)
				output += TwoPi;
			return output;
		}
		private static int _i, _j, _iLen, _jLen;
		/// <summary>
		/// Matrix multiplication. output = mat * colVec
		/// </summary>
		/// <param name="mat">A matrix with as many columns as the number of rows in colVec.</param>
		/// <param name="colVec">A vector with as many rows as the columns of mat</param>
		/// <param name="output">A column vector the same number of rows as mat.</param>
		public static void MM(float[,] mat, float[] colVec, float[] output)
		{
			_iLen = colVec.Length;
			_jLen = mat.GetLength(0);
			for(_j=0; _j<_jLen; _j++)
			{
				output[_j]=0.0f;
				for(_i=0; _i<_iLen; _i++)
				{
					output[_j]+=mat[_j,_i]*colVec[_i];
				}
			}
		}
		/// <summary>
		/// Matrix multiplication. output = rowVec* mat
		/// </summary>
		/// <param name="rowVec">A vector with as many columns as the rows of mat</param>
		/// <param name="mat">A matrix with as many rows as the columns in rowVec.</param>
		/// <param name="output">A row vector the same number of columns as mat.</param>
		public static void MM(float[] rowVec, float[,] mat, float[] output)
		{
			_iLen = mat.GetLength(1);
			_jLen = rowVec.Length;
			for(_i=0; _i<_iLen; _i++)
			{
				output[_i]=0.0f;
				for(_j=0; _j<_jLen; _j++)
				{
					output[_i]+=rowVec[_j]*mat[_j,_i];
				}
			}
		}

		/// <summary>
		/// Matrix multiplication. output = mat * colVec
		/// </summary>
		/// <param name="mat">A matrix with as many columns as the number of rows in colVec.</param>
		/// <param name="colVec">A vector with as many rows as the columns of mat</param>
		/// <param name="output">A column vector the same number of rows as mat.</param>
		public static void MM(double[,] mat, double[] colVec, double[] output)
		{
			_iLen = colVec.Length;
			_jLen = mat.GetLength(0);
			for(_j=0; _j<_jLen; _j++)
			{
				output[_j]=0.0f;
				for(_i=0; _i<_iLen; _i++)
				{
					output[_j]+=mat[_j,_i]*colVec[_i];
				}
			}
		}
		/// <summary>
		/// Matrix multiplication. output = rowVec* mat
		/// </summary>
		/// <param name="rowVec">A vector with as many columns as the rows of mat</param>
		/// <param name="mat">A matrix with as many rows as the columns in rowVec.</param>
		/// <param name="output">A row vector the same number of columns as mat.</param>
		public static void MM(double[] rowVec, double[,] mat, double[] output)
		{
			_iLen = mat.GetLength(1);
			_jLen = rowVec.Length;
			for(_i=0; _i<_iLen; _i++)
			{
				output[_i]=0.0f;
				for(_j=0; _j<_jLen; _j++)
				{
					output[_i]+=rowVec[_j]*mat[_j,_i];
				}
			}
		}
		public class Gaussian
		{
			private static double absz, density, t, cumulative;
			public static double Density(double z)
			{
				return .39894228040143*Math.Exp(-0.5*Math.Pow(z,2.0));
			}
			public static double Probability(double z)
			{
				absz = Math.Abs(z);
				density = .39894228*Math.Exp(-.5*Math.Pow((absz),2.0));
				t = 1.0/(1.0+0.33267*absz);
				cumulative =
					1.0 - density*(0.4361836*t-0.1201676*Math.Pow(t,2.0)+0.937298*Math.Pow(t,3.0));

				if(z<0)
					return 1.0-cumulative;
				else
					return cumulative;
			}
		}
		/// <summary>
		/// Differences f into df using forward and backward values of f (except for first and last values of f).
		/// </summary>
		/// <param name="f"></param>
		/// <param name="df"></param>
		/// <param name="len"></param>
		public static void Diff(double[] f, double[] df, int len)
		{
			if(len>1)
			{
				df[0]=f[1]-f[0];
				df[len-1]=f[len-2]-f[len-1];
				for(int i=1; i<len-2; i++)
					df[i]=(f[i+1]-f[i-1])/2.0;
			}
		}
		public static int _end;
		/// <summary>
		/// Differences f into df using forward and backward values of f (except for first and last values of f).
		/// This assumes that f is an angular parameter which cannot go outside of [-pi, pi]
		/// </summary>
		/// <param name="f"></param>
		/// <param name="df"></param>
		/// <param name="len"></param>
		public static void AngularDiff(double[] f, double[] df, int len)
		{
			if(len>1)
			{
				//	First value
				df[0]=f[1]-f[0];
				if(df[0]>Math.PI)
					df[0] = Util.TwoPi-df[0];
				else if(df[0]<-Math.PI)
					df[0] = Util.TwoPi+df[0];

				//	Last value
				_end = len-1;
				df[_end]=f[len-2]-f[_end];
				if(df[_end]>Math.PI)
					df[_end] = Util.TwoPi-df[_end];
				else if(df[_end]<-Math.PI)
					df[_end] = Util.TwoPi+df[_end];

				//	Middle values.
				for(int i=1; i<len-2; i++)
				{
					df[i]=(f[i+1]-f[i-1])/2.0;
					if(df[i]>Math.PI)
						df[i] = Util.TwoPi-df[i];
					else if(df[i]<-Math.PI)
						df[i] = Util.TwoPi+df[i];
				}
			}
		}
		public static float _dx1, _dx2, _dy1, _dy2, _dxTrail, _dyTrail, _dist;
		public static PointF _trailer;
		/// <summary>
		/// Moves the points right-wards by the given distance.  If the last
		/// point is equal to the first point, then a closed polygon is assumed
		/// (you get inward movement if the polygon's order is standardized).
		/// </summary>
		/// <param name="pts">A list of points to be moved (this is replaced by the output).</param>
		/// <param name="moveDist">The right-ward (or standardized inward) distance to move.</param>
		public static void MoveRightwards(PointF[] pts, float moveDist)
		{
			//	Record the first point to later see if this is a closed polygon.
			_trailer = pts[0];

			if(pts.Length<=1)
				return;
			_dx2 = pts[1].X-pts[0].X;
			_dy2 = pts[1].Y-pts[0].Y;
			_dist = (float)Math.Sqrt(_dx2*_dx2+_dy2*_dy2);
			_dx2/=_dist;
			_dy2/=_dist;

			_dxTrail = _dx2;
			_dyTrail = _dy2;
			
			pts[0] = new PointF(
				pts[0].X-moveDist*_dy2,	//	dx is actually the negative of the change in y.
				pts[0].Y+moveDist*_dx2 );	//	dy is actually the change in x.

			_iLen = pts.Length-1;
			for(_i=1; _i<_iLen; _i++)
			{
				_dx1 = _dx2;
				_dy1 = _dy2;
				_dx2 = pts[_i+1].X-pts[_i].X;
				_dy2 = pts[_i+1].Y-pts[_i].Y;
				_dist = (float)Math.Sqrt(_dx2*_dx2+_dy2*_dy2);
				_dx2/=_dist;
				_dy2/=_dist;
				pts[_i] = new PointF(
					pts[_i].X-moveDist*0.5f*(_dy1+_dy2),	//	dx is actually the change in y.
					pts[_i].Y+moveDist*0.5f*(_dx1+_dx2) );	//	dy is actually the negative of the change in x.
			}
			if(_trailer==pts[_iLen])	//	closed polygon, recompute pts[0]
			{
				pts[_iLen] = pts[0] = new PointF(
					_trailer.X-moveDist*0.5f*(_dyTrail+_dy2),	//	dx is actually the change in y.
					_trailer.Y+moveDist*0.5f*(_dxTrail+_dx2) );	//	dy is actually the negative of the change in x.
			}
			else	//	straight edge.
			{
				pts[_iLen] = new PointF(
					pts[_iLen].X-moveDist*_dy2,	//	dx is actually the change in y.
					pts[_iLen].Y+moveDist*_dx2 );	//	dy is actually the negative of the change in x.
			}
		}

		public class DistanceF
		{
			public static float dx, dy;
			public static float Distance(PointF p1, PointF p2)
			{
				dx = p1.X-p2.X;
				dy = p1.Y-p2.Y;
				return (float)Math.Sqrt(dx*dx+dy*dy);
			}
		}
		public delegate void ProgressUpdater(float progress);
		public static float[] LinSpace(float low, float high, float increment)
		{
			int length = (int)(Math.Abs((high-low)/increment)+0.5f)+1;
			float[] output = new float[length];
			for(int i=0; i<length; i++)
				output[i] = low + (float)i*increment;
			return output;
		}
		public static float[] LinSpace(float low, float high, int length)
		{
			float increment = (high-low)/(float)(length-1);
			float[] output = new float[length];
			for(int i=0; i<length; i++)
				output[i] = low + (float)i*increment;
			return output;
		}
		/// <summary>
		/// Factors using the simple sieve approach.  Based on Matlab's factor.m but improved.
		/// </summary>
		/// <param name="n">The number to be factored.</param>
		/// <returns>A vector of factors.</returns>
		public static int[] Factor(int n)
		{
			if(n<4)
				return new int[] {n};

			int[] p = Primes(n);

			if(n==p[p.Length-1])
				return new int[] {p[p.Length-1]};

			bool found=true;
			int i;

			ArrayList output = new ArrayList();
			while(n>1 && found)
			{
				found = false;
				//	Find any prime numbers that are divisible.
				for(i=0; i<p.Length; i++)
				{
					if( n%p[i] == 0 )
					{
						found=true;
						output.Add(p[i]);
						n/=p[i--];
					}
				}
			}
			return (int[])output.ToArray(typeof(int));
		}
		/// <summary>
		/// Generates a vector of prime numbers which are less than or equal to n.
		/// Code is based on Matlab's primes.m.
		/// </summary>
		/// <param name="n">The greatest number that can possibly be returned.</param>
		/// <returns>A vector of prime numbers.</returns>
		public static int[] Primes(int n)
		{
			if( n<2 )
				return new int[0];

			int i,k;

			//	The number of positive odd numbers less than or equal to n.
			int q = (n+1)/2;

			//	The list of odd numbers, also '2' is the first prime number (instead of '1')
			int[] possibilities = new int[q];
			possibilities[0] = 2;
			for(i=1; i<q; i++)
				possibilities[i] = 2*i+1;

			//	The largest possible factor of n.
			int sqrtn = (int)Math.Sqrt((double)n);

			//	Some algorithm taken from matlab's primes.m
			for(k=3; k<sqrtn; k+=2)
				if( possibilities[(k+1)/2-1] > 0 )
					for( i=(k*k+1)/2-1; i<q; i+=k)
						possibilities[i] = 0;

			//	All the non-zeros represent the answer.  Count them.
			int ct=0;
			for(i=0; i<q; i++)
				if(possibilities[i]>0)
					ct++;

			//	Generate output.
			int[] output = new int[ct];
			i=-1;
			k=0;
			while(k<ct)
				if(possibilities[++i]>0)
					output[k++]=possibilities[i];
			return output;
		}
	}
}
